import logging
import subprocess

from appium import webdriver
from appium.webdriver.common.mobileby import MobileBy
import time
import os
import base64
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.wait import WebDriverWait
from utils.logger import setup_logging
from utils.driver_helper import locate_element
from colorama import Fore, Style


class TestBase:
    def __init__(self):
        """
        初始化方法，设置日志记录并初始化测试结果列表。
        """
        self.driver = None
        setup_logging()
        self.test_results = []

    def setup_driver(self, platform_name, platform_version, app_package, app_activity, device_name):
        """
        设置 Appium WebDriver 以启动 Android 应用程序。

        参数:
            platform_name (str): 平台名称（例如 'Android'）。
            platform_version (str): 平台版本（例如 '11.0'）。
            app_package (str): 应用包名（例如 'com.example.app'）。
            app_activity (str): 应用启动 Activity（例如 '.MainActivity'）。
            device_name (str): 设备名称（例如 'emulator-5554'）。
        """
        caps = {
            "platformName": platform_name,
            "platformVersion": platform_version,
            "appPackage": app_package,
            "appActivity": app_activity,
            "deviceName": device_name,
            "automationName": "Uiautomator2",
            "skipServerInstallation": False,
            "skipDeviceInitialization": False,
            "noReset": True
        }
        self.driver = webdriver.Remote('http://localhost:4723', caps)

    def _scale_coordinate(self, coord, screen_size, key):
        """
        根据屏幕尺寸缩放坐标。

        参数:
            coord (float or int): 坐标值，如果是浮点数则按比例缩放。
            screen_size (dict): 屏幕尺寸，包含 'width' 和 'height'。
            key (str): 要缩放的维度键（'width' 或 'height'）。

        返回:
            int: 缩放后的坐标值。
        """
        return screen_size[key] * coord if isinstance(coord, float) else coord

    def swipe_screen(self, start_x, start_y, end_x, end_y, duration=None):
        """
        在屏幕上执行滑动操作。

        参数:
            start_x (float or int): 起始 X 坐标。
            start_y (float or int): 起始 Y 坐标。
            end_x (float or int): 结束 X 坐标。
            end_y (float or int): 结束 Y 坐标。
            duration (int, optional): 滑动持续时间（毫秒）。默认为 None。

        返回:
            bool: 如果滑动成功则返回 True，否则返回 False。
        """
        try:
            screen_size = self.driver.get_window_size()
            start_x = self._scale_coordinate(start_x, screen_size, 'width')
            start_y = self._scale_coordinate(start_y, screen_size, 'height')
            end_x = self._scale_coordinate(end_x, screen_size, 'width')
            end_y = self._scale_coordinate(end_y, screen_size, 'height')

            self.driver.swipe(start_x, start_y, end_x, end_y, duration)
            return True
        except Exception as e:
            logging.error(f"滑动操作失败: {str(e)}")
            return False, e

    def get_element_text(self, locator_type, locator_value):
        """
        获取指定元素的文本内容。

        参数:
            locator_type (str): 定位方式（如 'id', 'xpath', 'accessibility_id'）。
            locator_value (str): 定位值。

        返回:
            str or None: 元素的文本内容，如果元素未找到则返回 None。
        """
        element = locate_element(self.driver, locator_type, locator_value)
        return element.text if element else None

    def _find_element(self, locator_type, locator_value):
        """
        查找指定的元素。

        参数:
            locator_type (str): 定位方式（如 'id', 'xpath', 'accessibility_id'）。
            locator_value (str): 定位值。

        返回:
            WebElement or None: 找到的元素对象，如果没有找到则返回 None。
        """
        try:
            locator_map = {
                "id": MobileBy.ID,
                "xpath": MobileBy.XPATH,
                "accessibility_id": MobileBy.ACCESSIBILITY_ID
            }
            return self.driver.find_element(locator_map[locator_type], locator_value)
        except (KeyError, Exception):
            return None

    def is_element_exist(self, locator_type, locator_value, timeout=10):
        """
        判断指定的元素是否存在。

        参数:
            locator_type (str): 定位方式（如 'id', 'xpath', 'accessibility_id'）。
            locator_value (str): 定位值。
            timeout (int, optional): 等待元素出现的最大时间（秒）。默认为 10。

        返回:
            bool: 如果元素存在则返回 True，否则返回 False。
        """
        return bool(self._find_element(locator_type, locator_value))

    def is_toast_exist(self, text, timeout=10):
        """
        判断指定的 Toast 是否存在。

        参数:
            text (str): Toast 的文本内容。
            timeout (int, optional): 等待 Toast 出现的最大时间（秒）。默认为 10。

        返回:
            bool: 如果 Toast 存在则返回 True，否则返回 False。
        """
        try:
            toast_loc = f".//*[contains(@text,'{text}')]"
            self.driver.find_element(MobileBy.XPATH, toast_loc)
            return True
        except Exception as e:
            error_message = str(e)
            logging.error(error_message)
            return False, error_message

    def verify_toast_text(self, expected_text):
        """
        验证 Toast 的文本内容是否与预期相符。

        参数:
            expected_text (str): 预期的 Toast 文本。

        返回:
            bool: 如果实际文本与预期一致则返回 True，否则返回 False。
        """
        try:
            toast = WebDriverWait(self.driver, 10).until(
                ec.presence_of_element_located(("xpath", '//android.widget.Toast')))
            actual_text = toast.text
            if actual_text == expected_text:
                logging.info(f"Toast文本验证成功: 预期结果 '{expected_text}'，实际结果 '{actual_text}'")
                return True
            else:
                error = f"Toast文本验证失败: 预期结果 '{expected_text}'，实际结果 '{actual_text}'"
                logging.error(error)
                return False, error
        except Exception as e:
            error_message = f"无法获取Toast文本: {str(e)}"
            logging.error(error_message)
            return False, error_message

    def send_keys(self, key_code):
        """
        向设备发送按键事件。

        参数:
            key_code (int): 按键码。

        返回:
            bool: 如果按键操作成功则返回 True，否则返回 False。
        """
        try:
            self.driver.press_keycode(int(key_code))
            logging.info(f"按键操作成功: keycode={key_code}")
            return True
        except Exception as e:
            error_message = f"按键操作失败: {str(e)}"
            logging.error(error_message)
            return False, error_message

    def long_press_key(self, key_code):
        """
        向设备发送长按按键事件。

        参数:
            key_code (int): 按键码。

        返回:
            bool: 如果长按操作成功则返回 True，否则返回 False。
        """
        try:
            self.driver.long_press_keycode(int(key_code))
            logging.info(f"长按键操作成功: keycode={key_code}")
            return True
        except Exception as e:
            error_message = f"长按键操作失败: {str(e)}"
            logging.error(error_message)
            return False, error_message

    def print_step_result(self, case_id, step_number, test_step, success, error_msg=None):
        """
        打印测试步骤的结果信息。

        参数:
            case_id (str): 测试用例编号。
            step_number (int): 测试步骤编号。
            test_step (str): 测试步骤描述。
            success (bool): 测试步骤是否成功。
            error_msg (str, optional): 错误信息。默认为 None。
        """
        color = Fore.GREEN if success else Fore.RED
        status = "执行成功" if success else "执行失败"
        # print(f"{color}测试用例{case_id} - 步骤 {step_number} [{test_step}] - {status}{Style.RESET_ALL}")
        logging.info(f"{color}测试用例{case_id} - 步骤 {step_number} [{test_step}] - {status}{Style.RESET_ALL}")
        # if not success and error_msg:
        #     print(f"{color}错误信息: {error_msg}{Style.RESET_ALL}")

    @staticmethod
    def take_screenshot(driver, name=None):
        """
        截取屏幕截图并保存为 Base64 编码。

        参数:
            driver (WebDriver): WebDriver 实例。
            name (str, optional): 截图文件名。默认为当前时间戳。

        返回:
            str or None: Base64 编码的图片数据，如果截图失败则返回 None。
        """
        try:
            os.makedirs("screenshots", exist_ok=True)
            name = name or time.strftime("%Y%m%d_%H%M%S")
            file_path = os.path.join("screenshots", f"{name}.png")
            driver.get_screenshot_as_file(file_path)
            # logging.info(f"截图保存成功: {file_path}")

            with open(file_path, "rb") as image_file:
                return base64.b64encode(image_file.read()).decode()
        except Exception as e:
            error_message = f"截图失败: {str(e)}"
            logging.error(error_message)
            return None, error_message

    def take_screenshot_as_base64(self):
        """
        截取当前屏幕截图并返回其 Base64 编码。

        返回:
            str or None: Base64 编码的图片数据，如果截图失败则返回 None。
        """
        return self.take_screenshot(self.driver)

    def click_element(self, locator_type, locator_value):
        """
        点击指定的元素。

        参数:
            locator_type (str): 定位方式（如 'id', 'xpath', 'accessibility_id'）。
            locator_value (str): 定位值。

        返回:
            bool: 如果点击成功则返回 True，否则返回 False。
        """
        try:
            locator_map = {
                "id": MobileBy.ID,
                "xpath": MobileBy.XPATH,
                "accessibility_id": MobileBy.ACCESSIBILITY_ID
            }
            element = WebDriverWait(self.driver, 10).until(
                ec.element_to_be_clickable((locator_map[locator_type], locator_value))
            )
            element.click()
            return True
        except Exception as e:
            error_message = f"点击元素失败: {str(e)}"
            logging.error(error_message)
            return False, error_message

    def input_text(self, locator_type, locator_value, text):
        """
        向指定的输入框输入文本。

        参数:
            locator_type (str): 定位方式（如 'id', 'xpath', 'accessibility_id'）。
            locator_value (str): 定位值。
            text (str): 要输入的文本。

        返回:
            bool: 如果输入成功则返回 True，否则返回 False。
        """
        # element = locate_element(self.driver, locator_type, locator_value)
        locator_map = {
            "id": MobileBy.ID,
            "xpath": MobileBy.XPATH,
            "accessibility_id": MobileBy.ACCESSIBILITY_ID
        }
        element = WebDriverWait(self.driver, 10).until(
            ec.visibility_of_element_located((locator_map[locator_type], locator_value))
        )
        if element:
            try:
                element.clear()
                element.send_keys(text)
                return True
            except Exception as e:
                error_message = f"输入文本失败: {str(e)}"
                logging.error(error_message)
                return False, error_message

    def verify_element_text(self, locator_type, locator_value, expected_text):
        """
        验证元素的文本内容是否与预期相符。

        参数:
            locator_type (str): 定位方式（如 'id', 'xpath', 'accessibility_id'）。
            locator_value (str): 定位值。
            expected_text (str): 预期的元素文本。

        返回:
            bool: 如果实际文本与预期一致则返回 True，否则返回 False。
        """
        try:
            element = locate_element(self.driver, locator_type, locator_value)
            if element is None:
                error = f"元素未找到: {locator_type}={locator_value}"
                logging.error(error)
                return False, error

            actual_text = element.text
            if actual_text == expected_text:
                logging.info(f"元素文本验证成功: 预期结果 '{expected_text}'，实际结果 '{actual_text}'")
                return True
            else:
                error = f"元素文本验证失败: 预期结果 '{expected_text}'，实际结果 '{actual_text}'"
                logging.error(error)
                return False, error
        except Exception as e:
            error_message = f"无法获取元素文本: {str(e)}"
            logging.error(error_message)
            return False, error_message

    def execute_adb_command(self, command):
        """
        执行ADB命令

        参数:
            command (str): 要执行的ADB命令

        返回:
            tuple: 包含执行结果的元组 (success, output)，成功返回(True, 输出内容)，失败返回(False, 错误信息)
        """
        try:
            full_command = f'{command}'

            # 执行命令并获取输出
            result = subprocess.run(full_command, shell=True, capture_output=True, text=True, timeout=10)

            # 如果有错误信息则返回False和错误信息
            if result.stderr:
                return False, result.stderr

            # 否则返回True和标准输出
            return True, result.stdout
        except Exception as e:
            return False, str(e)

    def back_to_home(self):
        """
        返回到主界面
        """

        full_command = f'adb shell am start com.android.launcher3/com.android.launcher3.Launcher'
        subprocess.run(full_command, shell=True, capture_output=True, text=True, timeout=10)
        return True
